#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
A股交易约束模块（专业回测基线）

功能目标（面向真实市场约束的最小可用版本）：
- 涨跌停约束：涨停无法买入、跌停无法卖出（近似假设无对手盘）
- 停牌约束：停牌日不可成交
- T+1 限制：当日买入的持仓当日不可卖出（简化为按自然日判断）
- 信号-交易延迟：信号在收盘产生，次日开盘执行（或自定义延迟天数）

说明：
- 本模块仅提供纯函数与轻量数据结构，便于单元测试与引擎集成；
- 交易日历与更精细的撮合逻辑可在后续版本接入（如交易所日历、分时撮合等）。
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, date, timedelta
from typing import Optional


@dataclass
class MarketState:
    """市场状态（最小必要字段）

    Attributes:
        pre_close: 前收盘价
        upper_limit: 涨停价（含四舍五入后的交易价）
        lower_limit: 跌停价
        suspended: 是否停牌
    """

    pre_close: float
    upper_limit: float
    lower_limit: float
    suspended: bool = False


def compute_limit_prices(pre_close: float, limit_rate: float) -> tuple[float, float]:
    """根据前收盘与涨跌幅限制计算涨跌停价。

    备注：为避免小数误差，实际交易所会有价格步进与四舍五入规则，
    这里采用简单四舍五入到分（0.01）作为近似。
    """
    if pre_close <= 0:
        raise ValueError("pre_close 必须为正数")
    up = round(pre_close * (1 + limit_rate), 2)
    down = round(pre_close * (1 - limit_rate), 2)
    return up, down


def is_trade_blocked_by_limits(side: str, price: float, state: MarketState) -> bool:
    """判断是否因涨跌停被阻断成交。

    近似规则：
    - 买入：若价格大于等于涨停价，视为无法成交（无对手盘）
    - 卖出：若价格小于等于跌停价，视为无法成交（无对手盘）
    """
    s = side.lower()
    if s == "buy":
        return price >= state.upper_limit
    if s == "sell":
        return price <= state.lower_limit
    raise ValueError("side 仅支持 'buy' 或 'sell'")


def is_trade_blocked_by_suspension(state: MarketState) -> bool:
    """停牌则不可成交。"""
    return bool(state.suspended)


def can_sell_t_plus_one(buy_dt: date | datetime, today_dt: date | datetime) -> bool:
    """T+1 卖出限制（简化版：按自然日差值≥1）。

    注意：
    - 严谨实现需结合交易日历；此处先用自然日近似，确保基线可用。
    """
    buy_d = buy_dt.date() if isinstance(buy_dt, datetime) else buy_dt
    today_d = today_dt.date() if isinstance(today_dt, datetime) else today_dt
    return (today_d - buy_d) >= timedelta(days=1)


def next_execution_date(signal_dt: date | datetime, delay_days: int = 1) -> date:
    """将信号时间映射为执行日期（默认次日执行）。

    后续可替换为：遇到非交易日顺延至下一个交易日。
    """
    signal_d = signal_dt.date() if isinstance(signal_dt, datetime) else signal_dt
    return signal_d + timedelta(days=delay_days)


def can_execute_order(
    side: str,
    price: float,
    state: MarketState,
    *,
    is_suspended_ok: bool = False,
) -> bool:
    """综合判断订单是否可执行（不含资金/风控等其他约束）。

    Args:
        side: 'buy' 或 'sell'
        price: 拟成交价格（开/收等）
        state: 市场状态（含涨跌停/停牌）
        is_suspended_ok: 是否允许停牌成交（默认不允许）
    """
    # 停牌拦截
    if not is_suspended_ok and is_trade_blocked_by_suspension(state):
        return False

    # 涨跌停拦截
    if is_trade_blocked_by_limits(side, price, state):
        return False

    return True

